﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using Relay.UI.Controls.Win32;
using Relay.UI.Controls.Docking.Primitives;
using System.Windows.Controls;

namespace Relay.UI.Controls.Docking {
    internal class DockOperationService {
        #region Fields
        private Point _clickPos = new Point();
        private DockManager _manager = null;
        private FloatingWindow _window = null;
        private MouseEventHandler _moveHandler = new MouseEventHandler(OnMouseMove);
        private MouseButtonEventHandler _upHandler = new MouseButtonEventHandler(OnMouseUp);
        private IDropTarget _currentTarget = null;
        private IDropTarget[] _dropTargets = null;
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="DockOperationService"/> class.
        /// </summary>
        /// <param name="manager">The manager.</param>
        public DockOperationService(DockManager manager) {
            _manager = manager;
        }
        #endregion

        #region Public Methods
        public void Start(FloatingWindow window) {
            // A bit of a hack, but Mouse.GetPosition() is unreliable during drag operations
            Interop.POINTAPI mousePos = new Interop.POINTAPI();
            Interop.GetCursorPos(ref mousePos);

            _clickPos = new Point(mousePos.x - window.Left, SystemParameters.SmallCaptionHeight / 2);
            //_clickPos = new Point(Mouse.GetPosition(window).X, SystemParameters.SmallCaptionHeight / 2);

            List<IDropTarget> targets = new List<IDropTarget>();
            IDropTarget[] invalidTargets = ((IDockHierarchyObjectHost)window).FindDropTargets();
            targets.Add(_manager);
            targets.AddRange(((IDockHierarchyObjectHost)_manager).FindDropTargets());
            _dropTargets = targets.Where(x => !invalidTargets.Contains(x)).ToArray();

            _manager.CaptureMouse();
            _manager.MouseMove += new MouseEventHandler(OnMouseMove);
            _manager.MouseUp += new MouseButtonEventHandler(OnMouseUp);
            Application.Current.Deactivated += new EventHandler(DockOperationService_Deactivated);

            _window = window;
        }

        void DockOperationService_Deactivated(object sender, EventArgs e) {
            CompleteDockOperation();
        }
        #endregion

        #region Private Methods
        private void CompleteDockOperation() {
            _manager.MouseMove -= new MouseEventHandler(OnMouseMove);
            _manager.MouseUp -= new MouseButtonEventHandler(OnMouseUp);
            Application.Current.Deactivated -= new EventHandler(DockOperationService_Deactivated);

            Mouse.Capture(null);
            Point mousePos = _manager.PointToScreen(Mouse.GetPosition(_manager));

            if (_currentTarget != null) {
                _currentTarget.PerformDock(_window, mousePos);
            }

            foreach (IDropTarget target in _dropTargets) {
                target.End(_window);
            }

            _currentTarget = null;
        }

        private void OnMouseUp(MouseButtonEventArgs e) {
            CompleteDockOperation();
        }

        private void OnMouseMove(MouseEventArgs e) {
            Point mousePos = _window.PointToScreen(e.GetPosition(_window));

            _currentTarget = null;

            foreach (IDropTarget target in _dropTargets) {
                bool hitResult = target.Update(_window, mousePos, _currentTarget != null);

                if (hitResult)
                    _currentTarget = target;
            }

            _window.Visibility = (_currentTarget != null ? Visibility.Hidden : Visibility.Visible);

            _window.Left = mousePos.X - _clickPos.X;
            _window.Top = mousePos.Y - _clickPos.Y;
        }
        #endregion

        #region Event Handlers
        private static void OnMouseUp(object sender, MouseButtonEventArgs e) {
            ((DockManager)sender).DockService.OnMouseUp(e);
        }

        private static void OnMouseMove(object sender, MouseEventArgs e) {
            ((DockManager)sender).DockService.OnMouseMove(e);
        }
        #endregion
    }
}
